/*
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is ST_EXT-S_UIMAST.
 *
 * The Initial Developer of the Original Code is
 * University of Rome, Tor Vergata
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 */

package it.uniroma2.art.uimast.impl.modes.xpointer;

import java.util.Iterator;

import org.apache.uima.cas.CAS;

import it.uniroma2.art.ontapi.ARTRepository;
import it.uniroma2.art.ontapi.ARTResource;
import it.uniroma2.art.ontapi.vocabulary.XmlSchema;
import it.uniroma2.art.semanticturkey.resources.Resources;
import it.uniroma2.art.semanticturkey.vocabulary.SemAnnotVocab;
import it.uniroma2.art.uimast.impl.AnnotationProjection;
import it.uniroma2.art.uimast.xpts.ExportModeDriver;
import it.uniroma2.art.uimast.xpts.Variant;

/**
 * A driver providing the support for the default &quot;xpointer&quot; export
 * mpode.
 * @author <a href="mailto:manuel.fiorelli@gmail.com">Manuel Fiorelli</a>
 */
public class XPointerModeDriver implements ExportModeDriver {

	public String getModeName() {
		return "xpointer";
	}
	
	public CheckResult checkWiredProperty(ARTResource property, Variant v) {
		// Checks that the uri identifies one of the supported properties.
		String uri = property.getURI();
		if (uri.equals(IDENTITY_URI) || uri.equals(RANGE_URI)) {
			// Checks that the variant type is compatible with the property property range (whichis xsd:string)
			if (v.isCompatible(Resources.getRepository().createURIResource(XmlSchema.STRING))) {
				return CheckResult.OK;
			} else {
				return CheckResult.INCOMPATIBLE_RANGE;
			}
		}
		
		return CheckResult.UNSUPPORTED_WIRED_PROPERTY;
	}

	public void enterAnnotation(ARTResource domainObject, ARTResource annot) {
		// Gets the ontology repository interface.
		ARTRepository repository = Resources.getRepository();
		
		// Saves the domain object uri.
		identity = domainObject.getURI();
		
		// Saves the annotation range.
		range = repository.listSTDatatypePropertyValues(annot, SemAnnotVocab.Res.range).next().getLabel();
	}

	public void enterWebPage(ARTResource webPage, CAS cas) {
		ARTRepository repository = Resources.getRepository();
		cas.setSofaDataURI(repository.listSTDatatypePropertyValues(webPage,SemAnnotVocab.Res.url).next().getLabel(), "text/html");
	}

	public Iterator<Variant> getWiredProperty(ARTResource property, Variant v) {
		String uri = property.getURI();
		
		if (uri.equals(IDENTITY_URI)) {
			return createIterator(v, identity);
		} else if (uri.equals(RANGE_URI)) {
			return createIterator(v, range);
		} else
			throw new RuntimeException("Unknown wired property \"" + uri + "\"");
	}

	/**
	 * Creates an iterator over a sequence of lexical representation.
	 * 
	 * @param v
	 *             the variant to be used as prototype
	 * @param lexs
	 *             a sequence of lexical forms
	 * @return an iterator
	 */
	private Iterator<Variant> createIterator(final Variant v, final String ...lexs) {
		// Returns a new iterator.
		return new Iterator<Variant>() {

			// Checks if the value was read.
			public boolean hasNext() {
				return index < lexs.length;
			}

			// Gets the value (as a variant).
			public Variant next() {
				if (!hasNext()) {
					throw new IndexOutOfBoundsException(
							"Cannot dereference a past of the end iterator");
				} else {
					Variant w = v.clone();	// clones the prototype variant, to avoid unexpected behavior if the user stores it
					w.setValue(lexs[index++]);
					return w;
				}
			}

			// The remove operation isn't supported.
			public void remove() {
				throw new UnsupportedOperationException(
						"Cannot delete elements from a read only sequence");
			}

			// Index of the element to read
			private int index = 0;
		};
	}
	
	/**
	 * The current value of {@value XPointerModeDriver#IDENTITY_URI}
	 */
	private String identity;
	
	/**
	 * The current value of {@value XPointerModeDriver#RANGE_URI}
	 */
	private String range;
	
	/**
	 * The property {@code {@value XPointerModeDriver#IDENTITY_URI}}
	 */
	public static final String IDENTITY_URI = AnnotationProjection.PROJ_NS + "identity";

	/**
	 * The property {@code {@value XPointerModeDriver#RANGE_URI}}
	 */
	public static final String RANGE_URI = AnnotationProjection.PROJ_NS + "range";
}
